﻿//Filename: NegoServer.cs

/*
 * Auteur : Dominique GUERIN
 * dominiquephDOTguerinATgmailDOT..
 * dominiqueDOTguerinATinseeDOTfr(ance)  
 * Remerciements: à Keith Brown pour ses deux livres sur la sécurité sous Windows et ses différents articles
 *                 et à Michel Barnett de Microsoft pour ses deux articles 
 *                ".NET Remoting Authentication and Authorization Sample"  et le code qui les accompagne
 *  Le code est utilisable, modifiable et redistribuable à volonté sous la condition de ne pas supprimer ces 7 lignes.
 */ 
#define TRACE
using System;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Principal;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using System.Configuration;
using System.Collections.Specialized;
using System.Security.Cryptography.X509Certificates;
using System.ServiceProcess;
using System.Collections.Generic;

namespace httpToNegotiateStream
{
    /// <summary>
    /// To start and To stop the service
    /// </summary>
    internal class Spengo4Http : ServiceBase
    {
        internal static TraceSource log = new TraceSource("Spengo4Http");
        /// <summary>
        /// Called from Main: The parameter ( service name) is given by the Service Control Manager
        /// </summary>
        /// <param name="serviceName">Service Name enregistred in the registry</param>
        internal Spengo4Http(String serviceName)
        {
            this.ServiceName = serviceName;
            CanStop = true;
        }
        /// <summary>
        /// called by the Service Control Manager to start the service NegoServer. Create an instance of Server. There is only one instance. 
        /// </summary>
        /// <param name="args">Parameters given by the administrator when the service start: The service does not use it </param>
        protected override void OnStart(string[] args)
        {
            //server = new Server();
            //Thread threadInit = new Thread(server.StartServer);
            Thread threadInit = new Thread(Server.StartServer);
            threadInit.Start();
        }
        /// <summary>
        /// Called by the Service Control Manager to stop the service Negoserver. Stop the listener.
        /// </summary>
        protected override void OnStop()
        {
            try
            {

                //TcpListener listener = server.param.Listener;
                TcpListener listener = Server.param.Listener;
                if (listener != null)
                {
                    listener.Stop();
                    listener = null;
                }
            }
            catch (Exception e)
            {
                log.TraceEvent(TraceEventType.Verbose, 0, e.Message);
            }
        }
    }
    /// <remarks>
    /// <h2>Call Tree:</h2>
    /// <pre>
    /// Main a)Commnand line or b)Service
    ///   a)MainCommandLine
    ///   b)SPnegoHTTP.ctor
    ///     SetCurrentDirectory
    /// 
    /// Onstart
    ///  StartServer
    ///   ProcessClientRequest
    ///    Read and ReadContinue
    ///     TranslationOrAuthenticate //a)authenticate or b)Translate
    ///      a)AuthenticateClient 
    ///            SendNameAndSSP (via AuthenticateAsSersver)
    ///             ReadLengthOfTheSids (via Write)
    ///               PrepareReadSids (via Read)
    ///                 ValueOfTwoByte
    ///                   Server.ReuseTheSocketForAnotherRequest
    ///                   or 
    ///                   SendSidsState(via Read)
    ///                     CreateSids
    ///                     Compare
    ///                     Server.ReuseTheSocketForAnotherRequest (via Write)
    ///                         TryToReuseAConnection
    ///                           TranslationOrAuthenticate
    ///                             ....
    ///                     
    ///      b)Translation
    ///            TranslateAndSend (via ReadAtTheOffset)
    ///              TranslateCharsBigEndianToSids
    ///                TranslateCharsToSids
    ///                  PrepareTheSendResponseToTheWebServer
    ///                Server.ReuseTheSocketForAnotherRequest (via Write)
    ///                    TryToReuseAConnection
    ///                      TranslationOrAuthenticate
    ///                         ....
    /// </pre>         
    /// </remarks>
    /// <summary>
    /// <para>
    /// You can find traces of the example given on MSDN with the definition of the class NegotiateStream
    /// To communicate with Tomcat, I used the .NET NegotiateStream Protocol Specification in [MS-NNS.pdf]
    /// cf http://msdn.microsoft.com/en-us/library/cc236723(PROT.10).aspx
    /// </para>
    /// <para>
    /// I used the version of tomcatspnego with dll as bootstrap.
    /// </para>
    /// <list type="bullet">
    /// <item>
    /// First, I replaced the call of the DLL for the authentication by call 
    /// of <c>AsynchronousAuthenticatingTcpListener.AuthenticateClient</c> 
    /// and <c>AsynchronousAuthenticatingTcpListener.EndAuthenticate</c>.
    /// You can find traces of that in the java class SSPAuthentification: 
    ///    I used the client name "doume" and "OUF" as name of the Security Service Provider.
    /// </item>
    /// <item>
    /// Second, I used another TCP port to do the translation of the roles names into Sids. Java use Big Endian Unicode int their streams,
    /// and .Net uses Little Endian. 
    /// So the methods <see cref="TranslateServer.TranslateCharsBigEndianToSids">TranslateServer.TranslateCharsBigEndianToSids</see> 
    /// and <see cref="TranslateServer.TranslateCharsToSids">TranslateServer.TranslateCharsToSids</see>.
    /// </item>
    /// <item>
    /// Third, I add the Name of the client and the name of the Security Service Provider at the end of the 
    /// method <c>AsynchronousAuthenticatingTcpListener.EndAuthenticate</c>.
    /// </item>
    /// <item>
    /// Fourth, <see cref="AsynchronousAuthenticatingTcpListener.SendSidsState">AsynchronousAuthenticatingTcpListener.SendSidsState</see>, 
    /// <see cref="AsynchronousAuthenticatingTcpListener.CreateSids">AsynchronousAuthenticatingTcpListener.CreateSids</see> 
    /// and <see cref="AsynchronousAuthenticatingTcpListener.Compare">AsynchronousAuthenticatingTcpListener.Compare</see> 
    /// have been written to verify the pesence of the sids in the access token.
    /// </item>
    /// <item>
    /// Fifth, I used only one port. Tomcat send a byte to define the type of the request so the class <see cref="Server"/> and the methods <see cref="Server.TranslationOrAuthenticate"/>.
    /// </item>
    /// <item>
    /// Sixth, I replaced the Synchronous call to Asynchronous. So the class <see cref="State"> State</see>. We cannot use a loop to read. The local variables are now instance variables,
    /// <see cref="State.Read">State.Read</see> call <see cref="State.ReadContinue">State.ReadContinue</see>. 
    /// If the reading is not ended, <c>ReadContinue</c> call back <c>Read</c>. 
    /// When the reading is ended, <c>ReadContinue</c> call an instance of the delegate <see cref="NextToDo"/>. 
    /// The method called by the delegate is defined when an instance of the class<see cref="State">State</see> is created. 
    /// </item>
    /// <item>
    /// Seventh, I add a service, so the classes <see cref="NegoServer">NegoServer</see> and <see cref="Spengo4Http">Spengo4Http</see>. 
    /// I add explicit parameters, so the class <see cref="Parameters">Parameters</see>.
    /// </item>
    /// <item>
    /// Eigth, I added Time-out (9 seconds to read, 9 seconds to write, 20 seconds to authenticate (NTLM uses two round trips between Tomcat and NegoServer)
    /// </item>
    /// <item>
    /// Nineth, I added the possibility to reuse the connection TCP, 
    /// so <see cref="State.WaitTheNextRequest">State.WaitTheNextRequest</see> 
    /// and <see cref="Server.TryToReuseAConnection">Server.TryToReuseAConnection</see>.
    /// </item>
    /// <item>
    /// Tenth, I review the code. <see cref="AsynchronousAuthenticatingTcpListener.AuthenticateClient">
    /// <c>AsynchronousAuthenticatingTcpListener.AuthenticateClient</c></see>
    /// calls <see cref="State.AuthenticateAsServer"><c>AuthenticateAsServer</c></see>
    /// <c>AuthenticateAsServerContinue</c> calls <c>EndAuthenticateAsServer</c>.
    /// So all the Asynchronous Programming Model is managed by the class <see cref="State">State</see>
    /// AuthenticateAsServer calls <c>BeginAuthenticateAsServer</c> with the AsynCallBack 
    /// <see cref="State.AuthenticateAsServerContinue"><c>AuthenticateAsServerContinue</c></see>.
    /// </item>
    /// <item>
    /// Eleventh, I added the possibility to be authenticated with login/password for tomcat 7.
    /// NegotiateStream use MemoryStreams. So, it can be used withe the versions 2.0, 3.0, 3.5 and 4.0 of .NET
    /// <see cref="Server.StartServer"><c>Server.StartServer</c></see> calls 
    /// <see cref="AuthenticatingWithLogin.CreateStreamsToAuthenticateAndStackOfTheseStreamsWithBuffers">
    /// the method<c>CreateStreamsToAuthenticateAndStackOfTheseStreamsWithBuffers</c></see>.
    /// If there is an Exception it calls 
    /// <c>CreateStreamsToAuthenticateAndStackOfTheseStreamsWithTcp</c>.
    /// When Tomcat send the name and the password, Negoserver calls 
    /// <pre>
    ///     <c>AuthenticateClientWithLogin</c>
    ///         <c>GetLoginDomainPasswordInBigEndian</c>
    ///             <c>GetLoginPassword</c>
    ///                 <c>AuthenticateClientWithLoginAndNegotitaStream</c>
    ///                     <c>State.AuthenticateAsServerLogin</c>
    ///                         <c>AsynchronousAuthenticatingTcpListener.SendNameAndSSP</c>
    /// </pre>
    /// </item>
    /// <item>
    /// Twelveth, I rewrote the code. Now it is easier to test with the new beta pattern TPL.
    /// </item>
    /// </list>
    /// </summary>
    static class NegoServer
    {
        /// <summary>
        /// If exists an argument, it will start as a service. The parameter is the name of the service
        /// Without argument, the program can be launched in a command line 
        /// </summary>
        static void Main(String[] args)
        {
            SetCurrentDirectory();

            if (args.Length == 0)
            {
                Server.MainCommandLine();
                return;
            }

            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[] 
			{ 
				new Spengo4Http(args[0]) 
			};
            ServiceBase.Run(ServicesToRun);
        }
        ///<summary>
        /// Current Directory = path of Negoserver.exe
        ///  Used in the config file to define the loication of the log file
        ///</summary>
        static void SetCurrentDirectory()
        {
            Process p = Process.GetCurrentProcess();
            string filename = p.MainModule.FileName;
            int position = filename.LastIndexOf('\\');
            string path = filename.Substring(0, position + 1);
            Directory.SetCurrentDirectory(path);
        }
    }
    /// <summary>
    /// Get the parameters from the file Negoserver.exe.conf
    /// </summary>
    internal class Parameters
    {
        internal static TraceSource log = new TraceSource("Parameters");
        bool firstTime = true;
        TcpListener listener;

        int port = 21000;
        int numberthreadsinpool = 4 * 2;

        IPAddress ip = IPAddress.Any;
        bool isSsl = false;
        string serverName = null;
        //StoreName storename = StoreName.My;
        StoreLocation storelocation = StoreLocation.LocalMachine;
        bool iskerberosonly = false;
        int nb_connections_divided_nb_threads = 2;
        bool with_login_password = false;
        /// <summary>
        /// Read the parameters
        /// </summary>
        internal Parameters()
        {
            string parameter;
            NameValueCollection conf = ConfigurationManager.AppSettings;

            parameter = conf["port"];
            if (parameter != null)
            {
                if (Int32.TryParse(parameter, out port))
                {
                    log.TraceEvent(TraceEventType.Verbose, 0, "port :{0}", port);
                }
            }

            parameter = conf["numberthreadsinpool"];
            if (parameter != null)
            {
                if (Int32.TryParse(parameter, out numberthreadsinpool))
                {
                    log.TraceEvent(TraceEventType.Verbose, 0, "numberthreadsinpool :{0}", numberthreadsinpool);
                }
            }

            parameter = conf["ipAddress"];
            if (parameter != null)
            {
                if (IPAddress.TryParse(parameter, out ip))
                {
                    log.TraceEvent(TraceEventType.Verbose, 0, "ip_address :{0}", ip);
                }
                else
                {
                    ip = IPAddress.Any;
                    log.TraceEvent(TraceEventType.Error, 0, "ipAddress {0} is not an IP address! => IPAddress.Any is used");
                }
            }
            parameter = conf["sslservername"];
            if (parameter != null)
            {
                isSsl = true;
                serverName = parameter;
                log.TraceEvent(TraceEventType.Verbose, 0, "ssl_servername :{0}", serverName);
            }
            parameter = conf["storelocation_currentuser"];
            if (parameter != null)
            {
                storelocation = StoreLocation.CurrentUser;
                log.TraceEvent(TraceEventType.Verbose, 0, "StoreLocation.CurrentUser");
            }
            parameter = conf["onlykerberos"];
            if (parameter != null)
            {
                iskerberosonly = true;
            }
            parameter = conf["initial_size_poolofstreamsforloginpassword_divided_by_nb_threads"];
            if (parameter != null)
            {
                if (Int32.TryParse(parameter, out nb_connections_divided_nb_threads))
                {
                    log.TraceEvent(TraceEventType.Verbose, 0, "initial_size_poolofstreamsforloginpassword_divided_by_nb_threads :{0}", nb_connections_divided_nb_threads);
                }
            }
            parameter = conf["with_login_password"];
            if (parameter != null)
            {
                with_login_password = true;
                log.TraceEvent(TraceEventType.Verbose, 0, "withloginpassword");
            }
        }
        /// <summary>
        /// port defined in the parameters
        /// </summary>
        internal int Port
        {
            get { return port; }
        }
        /// <summary>
        /// numberthreadsinpool defined in the parameters:
        /// The number of idle worker threads and idle CompoletionPort threads is equal to numberthreadsinpool. 
        /// This number is used in the call of <c> ThreadPool.SetMinThreads</c>
        /// </summary>
        internal int NumberOfThreadsInPool
        {
            get { return numberthreadsinpool; }
        }
        /// <summary>
        /// Ip address of negoserver defined in the file <c>Negoserver.exe.conf</c>
        /// </summary>
        internal IPAddress Ip_Address{
            get { return ip; }
        }
        /// <summary>
        /// Name of the server: used to find the sertificat of the server, whenSSL is usde between Tomcat and Negoserver
        /// </summary>
        internal string ServerName{
            get{ return serverName; }
        }
        /// <summary>
        /// If there is a parameter <c>servername</c>, return true.
        /// </summary>
        /// <returns>true if <c>servername</c> exist in <c>Negoserver.conf.exe</c></returns>
        internal bool IsSsl()
        {
            return isSsl;
        }
        /// <summary>
        /// get by <see cref="Server.StartServer">Server.StartServer</see> 
        /// and <see cref="Server.ProcessClientRequest">Server.ProcessClientRequest</see>
        /// </summary>
        internal X509Certificate ServerCertificate
        {
            get
            {
                X509Store store = new X509Store(StoreName.My, ServerCertificatesLocation);
                store.Open(OpenFlags.ReadOnly);
                X509CertificateCollection certificates = store.Certificates.Find(X509FindType.FindBySubjectName, serverName, true);
                log.TraceEvent(TraceEventType.Verbose, 0, "==== Count of server certificates: {0}", certificates.Count);
                X509Certificate certserver = null;
                foreach (X509Certificate cert in certificates)
                {
                    certserver = cert;
                }
                return certserver;
            }
        }
        /// <summary>
        /// Used by <see cref="Spengo4Http.OnStop">Spengo4Http.OnStop</see>. 
        /// Set by <see cref="Server.StartServer">Server.StartServer</see>
        /// </summary>
        internal TcpListener Listener
        {
            get { return listener; }
            set { listener = value; }
        }
        /// <summary>
        /// get by <see cref="ServerCertificate">ServerCertificate</see>.
        /// When the parameter <c>storelocation</c> is  present in the configuration file, return <c>StoreLocation.CurrentUser</c>
        /// else return <c>StoreLocation.LocalMachine</c>
        /// </summary>
        internal StoreLocation ServerCertificatesLocation
        {
            get { return storelocation; }
        }
        /// <summary>
        /// get by <see cref="AsynchronousAuthenticatingTcpListener.AuthenticateClient">AsynchronousAuthenticatingTcpListener.AuthenticateClient</see>
        /// </summary>
        /// <returns>When the parameter <c>onlykerberos</c> is present, return true.</returns>
        internal bool IsKerberosOnly()
        {
            return iskerberosonly;
        }
        ///<summary>
        ///If return true, 
        /// <returns>When the parameter <c>with_login_password</c> is present, return true.</returns>   
        ///</summary>
        internal bool IsWithLoginPassword()
        {
            return with_login_password;
        }
        /// <summary>
        /// return the parameter but only between 2 and 5
        /// used by <see cref="AuthenticatingWithLogin.CreateStreamsToAuthenticateAndStackOfTheseStreamsWithBuffers"/>
        /// CreateStreamsToAuthenticateAndStackOfStreamsWithTcp
        /// </summary>
        internal int NbConnectionsDividedByNbThreads
        {
            get 
            {
                int max = Math.Min(5, nb_connections_divided_nb_threads);
                return Math.Max(2, max);
            }
        }
        /// <summary>
        /// get by <see cref="Server.ProcessClientRequest">Server.ProcessClientRequest</see>. Log only in the first call.
        /// </summary>
        /// <param name="sslstream"></param>
        internal void DisplaySslInfo(SslStream sslstream)
        {
            if (firstTime)
            {
                DisplaySecurityLevel(sslstream);
                DisplaySecurityServices(sslstream);
                //param.DisplayCertificateInformation(sslStream);
                DisplayStreamProperties(sslstream);
            }
            firstTime = false;
        }
        /// <summary>
        /// called from <see cref="DisplaySslInfo">DisplaySslInfo</see>
        /// </summary>
        /// <param name="stream"> SslStream</param>
        private void DisplaySecurityLevel(SslStream stream)
        {
            log.TraceEvent(TraceEventType.Verbose, 0, "Cipher: {0} strength {1}", stream.CipherAlgorithm, stream.CipherStrength);
            log.TraceEvent(TraceEventType.Verbose, 0, "Hash: {0} strength {1}", stream.HashAlgorithm, stream.HashStrength);
            log.TraceEvent(TraceEventType.Verbose, 0, "Key exchange: {0} strength {1}", stream.KeyExchangeAlgorithm, stream.KeyExchangeStrength);
            log.TraceEvent(TraceEventType.Verbose, 0, "Protocol: {0}", stream.SslProtocol);
        }
        /// <summary>
        /// called from <see cref="DisplaySslInfo">DisplaySslInfo</see>
        /// </summary>
        /// <param name="stream">SslStream</param>
        private void DisplaySecurityServices(SslStream stream)
        {
            log.TraceEvent(TraceEventType.Verbose, 0, "Is authenticated: {0} as server? {1}", stream.IsAuthenticated, stream.IsServer);
            log.TraceEvent(TraceEventType.Verbose, 0, "IsSigned: {0}", stream.IsSigned);
            log.TraceEvent(TraceEventType.Verbose, 0, "Is Encrypted: {0}", stream.IsEncrypted);
        }
        /// <summary>
        /// called from <see cref="DisplaySslInfo">DisplaySslInfo</see>
        /// </summary>
        /// <param name="stream">SslStream</param>
        private void DisplayStreamProperties(SslStream stream)
        {
            log.TraceEvent(TraceEventType.Verbose, 0, "Can read: {0}, write {1}", stream.CanRead, stream.CanWrite);
            log.TraceEvent(TraceEventType.Verbose, 0, "Can timeout: {0}", stream.CanTimeout);
        }        
    }
    /// <summary>
    /// <see cref="Server">Server</see> manage the listener, the creation of TCPClient 
    /// via the method <see cref="TcpListener.AcceptTcpClient">TcpListener.AcceptTcpClient</see>
    /// the begin of the process of the request via <see cref="ProcessClientRequest">ProcessClientRequest</see>, 
    /// or the reuse of a request via <see cref="TryToReuseAConnection">TryToReuseAConnection</see>. Then the dispaching according the type of the request
    /// via <see cref="TranslationOrAuthenticate">TranslationOrAuthenticate</see>, and then call 
    /// <see cref="AsynchronousAuthenticatingTcpListener.AuthenticateClient">AsynchronousAuthenticatingTcpListener.AuthenticateClient</see>
    /// or <see cref="TranslateServer.Translation">TranslateServer.Translation</see>.
    /// </summary>
    /// <remarks>
    /// <h2>Call tree:</h2>
    /// <pre>
    ///  StartServer (First time)
    ///   TcpListener.AcceptTcpClient (in a loop to create a new connexion with a client)
    ///    ProcessClientRequest
    ///     Read and ReadContinue
    ///      TranslationOrAuthenticate
    /// or
    ///  TryToReuseAConnection (to reuse a connection)
    ///   Read and ReadContinue
    ///    TranslationOrAuthenticate
    ///     </pre>
    /// </remarks>
    internal class Server
    {
        internal static TraceSource log = new TraceSource("Server");
        /// <summary>
        /// One byte is sent by Tomcat to define the type of the request
        /// </summary>
        internal enum ConnectionType : byte
        {
            /// <summary>
            /// Translate the tomcat roles into Sids
            /// used by <see cref="Server.TranslationOrAuthenticate">Server.TranslationOrAuthenticate</see>
            /// </summary>
            TranslationNameGroupsToSids,
            /// <summary>
            /// Authenticate the client
            /// used by <see cref="Server.TranslationOrAuthenticate">Server.TranslationOrAuthenticate</see>
            /// </summary>
            Authentication,
            /// <summary>
            /// Reuse the connection. used by <see cref="Server.TryToReuseAConnection">Server.TryToReuseAConnection</see>
            /// </summary>
            ReuseAConnectionTcp,
            /// <summary>
            /// Authenticate the client, but inform tomcat if the user is member of the Authenticated Users 
            /// used by <see cref="Server.TranslationOrAuthenticate">Server.TranslationOrAuthenticate</see>
            /// </summary>
            AuthenticationAndInformIfAuthenticatedUser,
            /// <summary>
            /// Authenticate the client with a login/Password, but inform tomcat if the user is member of the Authenticated Users 
            /// used by <see cref="Server.TranslationOrAuthenticate">Server.TranslationOrAuthenticate</see>
            /// </summary>
            AuthenticationLogin
        }
        /// <summary>
        /// To retrieve the parameters, and the listener in the <see cref="Spengo4Http.OnStop">Spengo4Http.OnStop</see>
        /// </summary>
        internal static Parameters param;
        /// <summary>
        /// Called from <see cref="NegoServer.Main">NegoServer.Main</see> if there is no argument
        /// </summary>
        internal static void MainCommandLine() 
        {
            //Server server = new Server();
            //Thread threadInit = new Thread(server.StartServer);
            Thread threadInit = new Thread(StartServer);
            threadInit.Start();
        }
        /// <summary>
        /// Is invoked by Program.Main or MainCommandLine via a new foreground thread,
        /// so the application will not stop if this thread is running.
        /// All the other threads are in backgroud state.
        /// Create a tcplistener and then a loop to accept the  requessts of the clients
        /// </summary>
        internal static void StartServer()
        {
            Parameters parameters = new Parameters();
            try
            {
                log.TraceEvent(TraceEventType.Verbose, 0, "CLR version: {0}", Environment.Version);
                log.TraceEvent(TraceEventType.Verbose, 0, "If is running as an x64 exe, size of the pointers = 8, else 4. The Size of the pointers: {0}", IntPtr.Size);
                log.TraceEvent(TraceEventType.Verbose, 0, "Current Directory: {0}", Directory.GetCurrentDirectory());
            }           
            catch
                //You cannot use log
            {
                log.Switch.Level = SourceLevels.Off;
                Parameters.log.Switch.Level = SourceLevels.Off;
                Spengo4Http.log.Switch.Level = SourceLevels.Off;
                AsynchronousAuthenticatingTcpListener.log.Switch.Level = SourceLevels.Off;
                TranslateServer.log.Switch.Level = SourceLevels.Off;//
                //ClientState.log.Switch.Level = SourceLevels.Off;
                State.log.Switch.Level = SourceLevels.Off;              
            }
            int numberOfThreads = parameters.NumberOfThreadsInPool;
            int workerThreads = 0;
            int completionPortThreads = 0;;

            ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
            numberOfThreads = Math.Max(Math.Max(numberOfThreads, workerThreads), completionPortThreads);
            ThreadPool.SetMinThreads(numberOfThreads, numberOfThreads);

            ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
            log.TraceEvent(TraceEventType.Verbose, 0, "WorkerThreads: " + workerThreads + ", completionPortThreads: " + completionPortThreads);

            try 
            {
                if (parameters.IsSsl() )
                {
                    log.TraceEvent(TraceEventType.Verbose, 0, "ServerName {0}, length: {1}",
                        parameters.ServerName, parameters.ServerName.Length);
                    X509Certificate certif = parameters.ServerCertificate;
                    if (certif == null)
                    {
                        log.TraceEvent(TraceEventType.Critical, 0, "No server certificate");
                        return;
                    }
                    log.TraceEvent(TraceEventType.Verbose, 0, certif.Subject);
                }
            }
            catch (Exception e)
            {
                log.TraceEvent(TraceEventType.Critical, 0, e.Message + " " + e.StackTrace);
                log.TraceEvent(TraceEventType.Critical, 0, "Certicat serveur incorrect");
                return;
            }
            try
            {
                if (parameters.IsWithLoginPassword())
                {
                    int factor = parameters.NbConnectionsDividedByNbThreads;
                    AuthenticatingWithLogin.CreateStreamsToAuthenticateAndStackOfTheseStreamsWithBuffers(numberOfThreads, factor);
                }
            }
            catch (Exception e)
            {
                log.TraceEvent(TraceEventType.Verbose, 0, e.GetType() + "  " + e.Message + " " + e.StackTrace);
                return;
            }
            IPEndPoint endpoint;
            try
            {
                endpoint = new IPEndPoint(parameters.Ip_Address, parameters.Port);
            }
            catch (Exception e)
            {
                log.TraceEvent(TraceEventType.Critical, 0, e.Message + " " + e.StackTrace);
                return;
            }
            TcpListener listener;
            try
            {
                listener = new TcpListener(endpoint);
            }
            catch (SocketException e)
            {
                log.TraceEvent(TraceEventType.Critical, 0, "listener cannot listen! {0}  Native Error Code : {1} Socket Error Code : {3}", 
                    e.Message, e.NativeErrorCode, e.SocketErrorCode );
                listener = null;
                return;
            }
            // Listen for incoming connections.
            try
            {
                //listener.Start(20);// Maximum pending connections : 20 connections
                listener.Start();
            }
            catch (SocketException e)
            {
                log.TraceEvent(TraceEventType.Critical, 0, "listener cannot start! {0}  Native Error Code : {1} Socket Error Code : {3}",
                    e.Message, e.NativeErrorCode, e.SocketErrorCode);
                listener = null;
                return;
            }
            parameters.Listener = listener;
            param = parameters;

            try
            {
                while (true)
                {
                    TcpClient clientRequest = null;
                    // Application blocks while waiting for an incoming connection.
                    // Type CNTL-C to terminate the server launched with a command line.
                    clientRequest = listener.AcceptTcpClient();
                    //only for synchronous send or receive => these parametres are not used
                    //clientRequest.Client.SendTimeout = 1000 * 20; //20 secondes
                    //clientRequest.Client.ReceiveTimeout = 1000 * 60 * 6; // 6 minutes
                    // A client has connected. 
                    RequestState requeststate = new RequestState(clientRequest, parameters);
                    try
                    {                           
                        ThreadPool.QueueUserWorkItem(ProcessClientRequest, requeststate);
                    }
                    catch (Exception e)
                    {
                        log.TraceEvent(TraceEventType.Verbose, requeststate.ClientPort, e.Message + " " + e.StackTrace);
                        //continue;
                    }
                    log.TraceEvent(TraceEventType.Verbose, requeststate.ClientPort, "To a new request with a new connection TCP");
                }
            }
            catch (SocketException e)
            {
                log.TraceEvent(TraceEventType.Verbose, 0, e.Message + " " + e.NativeErrorCode + e.SocketErrorCode);
            }
        }
        /// <summary>
        /// Called from <see cref="StartServer">StartServer</see>. 
        /// If isSsl, create a SslStream, then call <see cref="State.Read">State.Read</see> to read one byte,  
        /// with the delegate <see cref="NextToDo">NextToDo</see> set to 
        /// <see cref="Server.TranslationOrAuthenticate">Server.TranslationOrAuthenticate</see>
        /// </summary>
        /// <param name="reqState">State of the request (TcpClient, Parametsr)</param>
        internal static void ProcessClientRequest(object reqState)
        {
            RequestState rs = (RequestState)reqState;
            Parameters param = rs.Param;
            TcpClient clientRequest = rs.Client;
            Stream stream = null;
            try
            {
                NetworkStream nstream = clientRequest.GetStream();
                stream = nstream;
                if (param.IsSsl())
                {
                    SslStream sslstream = new SslStream(nstream, false);// leaveInnerStream == false
                    stream = sslstream;
                    sslstream.AuthenticateAsServer(param.ServerCertificate, false, SslProtocols.Default, false);
                    param.DisplaySslInfo(sslstream);
                }
            }
            catch (Exception e)
            {
                log.TraceEvent(TraceEventType.Verbose, rs.ClientPort, e.Message + " " + e.StackTrace);
                try
                {   
                    stream.Close();
                    clientRequest.Close();
                    log.TraceEvent(TraceEventType.Verbose, rs.ClientPort, "Close the connection");
                }
                catch (Exception ex)
                {
                    log.TraceEvent(TraceEventType.Verbose, rs.ClientPort, ex.Message + " " + ex.StackTrace);
                }
                return;
            }


            log.TraceEvent(TraceEventType.Verbose, rs.ClientPort, "Before TranslationOrAuthenticate");
            //Read the first byte and then call TranslationOrAuthenticate. This byte define the type of the request:
            // Translation Tomcat roles into Sids or Authentication
            State s = new State(stream, clientRequest, param.IsKerberosOnly(), param.IsWithLoginPassword());
            byte[] buf = new byte[1];
            s.Read(buf, TranslationOrAuthenticate);

        }
        /// <summary>
        /// Call <see cref="TranslationOrAuthenticate">TranslationOrAuthenticate</see> after verification of the bytes received from tomcat
        /// </summary>
        /// <param name="s">State to find the array of the received bytes</param>
        internal static void TryToReuseAConnection(State s)
        {
            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "In TryToReuseAConnection");
            byte[] header = s.Buffer;
            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "Buffer.length{0}", s.Buffer.Length);
            switch (header[0])
            {
                case (byte) ConnectionType.ReuseAConnectionTcp:
                    log.TraceEvent(TraceEventType.Verbose, s.ClientPort,"ConnectionType.Test");
                    //Read on byte and after call TranslationOrAuthenticate
                    byte[] buf = new byte[1];
                    s.Read(buf, TranslationOrAuthenticate);
                    break;
                default:
                    log.TraceEvent(TraceEventType.Error, s.ClientPort, "First byte received is not ConnectionType.Test : {0}", header[0]);
                    s.CleanUp();
                    break;
            }
        }
        /// <summary>
        /// Dispatch to Authentication via <see cref="AsynchronousAuthenticatingTcpListener.AuthenticateClient">AsynchronousAuthenticatingTcpListener.AuthenticateClient</see>
        /// or Translation via <see cref="State.Read">State.Read</see> with <see cref="NextToDo">NextToDo</see>
        /// set to <see cref="TranslateServer.Translation">TranslateServer.Translation</see>
        /// </summary>
        /// <param name="s">State</param>
        internal static void TranslationOrAuthenticate(State s)
        {
            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "In TranslationOrAuthenticate");
            byte[] header = s.Buffer;
            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "Buffer.length {0}", s.Buffer.Length);
            byte[] buf;
            switch (header[0])
            {
                case (byte)ConnectionType.TranslationNameGroupsToSids:
                    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "ConnectionType.TranslationNameGroupsToSids");
                    //Read two bytes(count of bytes) and then call Translation
                    buf = new byte[2];
                    s.Read(buf, TranslateServer.Translation);
                    break;
                case (byte)ConnectionType.Authentication:
                    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "ConnectionType.Authentication");
                    AsynchronousAuthenticatingTcpListener.AuthenticateClient(s);
                    //s.AuthenticateClient(AsynchronousAuthenticatingTcpListener.PrepareNameAndSSP);
                    break;
                case (byte)ConnectionType.AuthenticationAndInformIfAuthenticatedUser:
                    s.MustInformIfAuthenticatedUser = true;
                    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "ConnectionType.AuthenticationAndInformIfAuthenticatedUser");
                    AsynchronousAuthenticatingTcpListener.AuthenticateClient(s);
                    break;
                case (byte)ConnectionType.AuthenticationLogin:
                    if (!s.IsWithLogin())
                    {
                        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "Authentication with login/password is not accepted: Add the parameter with_login_password");
                        s.CleanUp();
                        return;
                    }
                    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "ConnectionType.AuthenticationLogin");
                    //Read two bytes(count of bytes) and then call AuthenticateClientWithLogin
                    //s = new State(s, AuthenticatingWithLoginTcpListener.AuthenticateClientWithLogin, 2);
                    s.MustInformIfAuthenticatedUser = true;
                    buf = new byte[2];
                    s.Read(buf, AuthenticatingWithLogin.AuthenticateClientWithLogin);
                    break;
                default:
                    log.TraceEvent(TraceEventType.Error, s.ClientPort, "First byte received is not 0 or 1"); 
                    s.CleanUp();
                    break;
            }
        }
        /// <summary>
        /// The following method is invoked by the
        /// Write in <see cref="AsynchronousAuthenticatingTcpListener.SendNameAndSSP">SendNameAndSSP</see> and in <see cref="AsynchronousAuthenticatingTcpListener.SendSidsState">SendSidsState</see>.
        /// Call <see cref="State.WaitTheNextRequest">State.WaitTheNextRequest</see> with the delegate <see cref="NextToDo">NextToDo</see> 
        /// set to <see cref="Server.TryToReuseAConnection"> Server.TryToReuseAConnection </see>. 
        /// </summary>
        /// <param name="state">The state</param>
        internal static void ReuseTheSocketForAnotherRequest(State state)
        {

            log.TraceEvent(TraceEventType.Verbose, state.ClientPort, "Connection to authenticate is reused");
            log.TraceEvent(TraceEventType.Verbose, state.ClientPort, "Before TranslationOrAuthenticate");
            //try to reuse this connection to another request (so call TryToReuseAConnection)
            state.WaitTheNextRequest(Server.TryToReuseAConnection);
        }
    }
    /// <summary>
    /// Class used to process the requests to authenticate the client, to send the name of the client 
    /// and of the Security Service Provider. Give the list of the Tomcat roles wich are referenced in the client's access token.
    /// </summary>
    /// <remarks>
    /// <h2>Call tree:</h2>
    /// <pre>
    ///        AuthenticateClient 
    ///            SendNameAndSSP (via AuthenticateAsSersver)
    ///             ReadLengthOfTheSids (via Write)
    ///               PrepareReadSids (via Read)
    ///                 ValueOfTwoByte
    ///                   Server.ReuseTheSocketForAnotherRequest
    ///                   or 
    ///                   SendSidsState(via Read)
    ///                     CreateSids
    ///                     Compare
    ///                     Server.ReuseTheSocketForAnotherRequest (via Write)
    /// </pre>
    /// </remarks>
    internal static class AsynchronousAuthenticatingTcpListener
    {
        internal static TraceSource log = new TraceSource("AsynchronousAuthenticatingTcpListener");
        /// <summary>
        /// <para>
        /// Called from <see cref="Server.TranslationOrAuthenticate">Server.TranslationOrAuthenticate</see>.
        /// </para>
        /// <para>
        /// Call <c>state.AuthenticateAsServer</c> with the <c>NextToDo</c> 
        /// set to <see cref="SendNameAndSSP">SendNameAndSSP</see>. 
        /// </para>
        /// <para>
        /// If <c>requestState.IsKerberosOnly</c> is true, <c>ProtectionLevel</c> 
        /// is <see cref="ProtectionLevel.EncryptAndSign">ProtectionLevel.EncryptAndSign</see> , 
        /// else <see cref="ProtectionLevel.None">ProtectionLevel.None</see>. 
        /// </para>        
        /// <para>
        /// In Reality, when Ntlm via Spnego, 
        /// <c>ProtectionLevel</c> will be after the negotiation<see cref="ProtectionLevel.Sign"/>. 
        /// But without Spnego, with the Tomcat parameter <c>onlyntlm</c>,
        /// <c>ProtectionLevel</c> will be finally <see cref="ProtectionLevel.None">ProtectionLevel.None</see>
        /// </para>
        /// </summary>
        /// <param name="state"></param>
        internal static void AuthenticateClient( State state)
        {
            state.AuthenticateAsServer(SendNameAndSSP);
        }
        internal static void SendNameAndSSP(State s)
        {
            try
            {
                bool userAuthenticated = false;
                WindowsIdentity idClient = s.IdClient; 
                if (idClient != null)
                {
                    // copy the Name of the Client and the name of the Security Service Provider to buf via a MemoryStream
                    //prepare the BinaryWriter and the memoryStream : use BIG_ENDIAN (java use BIG_ENDIAN)
                    int size_chars_to_send = idClient.Name.Length + idClient.AuthenticationType.Length + 2;
                    if (s.MustInformIfAuthenticatedUser) size_chars_to_send++;
                    int size_bytes_to_send = size_chars_to_send * 2;
                    byte[] buf = new byte[size_bytes_to_send];
                    MemoryStream ms = new MemoryStream(buf);
                    BinaryWriter bi = new BinaryWriter(ms, Encoding.BigEndianUnicode);
                    char cNameLength = (char)(ushort)idClient.Name.Length;
                    char cAuthType = (char)(ushort)idClient.AuthenticationType.Length;

                    //copy to the MemoryStream 
                    bi.Write(cNameLength);
                    bi.Write(idClient.Name.ToCharArray());
                    bi.Write(cAuthType);
                    bi.Write(idClient.AuthenticationType.ToCharArray());
                    if (s.MustInformIfAuthenticatedUser)
                    {
                        userAuthenticated = isUserAuthenticated(s);
                        char cIsAuthenticated = (char)(ushort)(userAuthenticated ? 1 : 0);
                        bi.Write(cIsAuthenticated);
                    }
                    bi.Flush();

                    //s.IdClient = idClient;
                    if (s.MustInformIfAuthenticatedUser && !userAuthenticated)
                    {
                        s.Write(buf, Server.ReuseTheSocketForAnotherRequest);
                        return;
                    }
                    else
                    {
                        //send the content of buf
                        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "Before to send Inormations Name + SSP");
                        s.Write(buf, ReadLengthOfTheSids);
                        return;
                    }
                }
                else //idClient == null
                {
                    s.CleanUp();
                    return;
                }
            }
            catch (Exception e)
            {
                s.ProcessException(e);
                return;
            }

        }
        /// <summary>
        /// The following method is invoked by the
        /// Write in <see cref="SendNameAndSSP">SendNameAndSSP</see>.
        /// Call <see cref="State.Read">State.Read</see> to get the the count of bytes to read, 
        /// with the delegate <see cref="NextToDo">NextToDo</see>
        /// set to <see cref="PrepareReadSids">PrepareReadSids</see> to read the value.
        /// </summary>
        /// <param name="state">The state</param>
        internal static void ReadLengthOfTheSids(State state)
        {
            //State sState = new State(state, PrepareReadSids, 2);
            //sState.Read();
            byte[] buf = new byte[2];
            log.TraceEvent(TraceEventType.Verbose, state.ClientPort, "Before to read the length of the sids");
            state.Read(buf, PrepareReadSids);
        }       
        /// <summary>
        /// This metod reads the length of the SIds sent by tomcat, and then read the bytes. 
        /// The method <see cref="SendSidsState">SendSidsState</see> 
        /// is called after <see cref="State.ReadContinue">State.ReadContinue</see>.
        /// </summary>
        /// <param name="state"></param>
        internal static void PrepareReadSids( State state)
        {
            int length;
            //Cont of bytes
            length = ValueOfTwoBytes(0, state.Buffer);
            log.TraceEvent(TraceEventType.Verbose, state.ClientPort, "length of the received array of sids {0}", length);
            if (length == 0)
            {
                Server.ReuseTheSocketForAnotherRequest(state);
                return;
            }
            //Read all the bytes (content: Sids) and then call SendSidsState
            byte[] buf = new byte[length];
            state.Read(buf, SendSidsState);
        }
        /// <summary>
        /// Translate two bytes in int
        /// </summary>
        /// <param name="position">position of the two bytes in the buffer </param>
        /// <param name="tab">buffer</param>
        /// <returns>the value in int</returns>
        private static int ValueOfTwoBytes(int position, byte[] tab)
        {
            int highByte, lowByte;

            int pos_next = position++;

            highByte = tab[pos_next];
            highByte &= 0xff;
            highByte = highByte << 8;
            lowByte = ((int)tab[position]) & 0xff;

            return highByte + lowByte;
        }        
        ///<summary>
        /// Call <see cref="CreateSids">CreateSids</see> (to create Sids with the bytes received from tomcat),
        /// and call <see cref="Compare">Compare</see> to compare these Sids to the Sids of the groups wich are referenced in the client's access token.
        /// it calls <c>BeginWrite</c> with the callback <see cref="Server.ReuseTheSocketForAnotherRequest">ReuseTheSocketForAnotherRequest</see> to send the result to tomcat.
        /// </summary>
        internal static void SendSidsState(State s)
        {
            byte[] sids_array_of_bytes = s.Buffer;
            //Translate the bytes into SIDs
            SecurityIdentifier[] sids = CreateSids(sids_array_of_bytes, s);
            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "count of sids {0}", sids.Length);

            byte[] present_groups = null;

            try
            {
            //Verify user is authenticated
            //bool _isUserAuthenticated = false;
            //foreach (IdentityReference id in s.IdClient.Groups)
            //{
            //    SecurityIdentifier sid = id as SecurityIdentifier;
            //    if (!_isUserAuthenticated && sid != null)
            //    {
            //        if (sid.IsWellKnown(WellKnownSidType.AuthenticatedUserSid)) _isUserAuthenticated = true;
            //    }
            //}
            //if (_isUserAuthenticated) log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "User IS Authenticated");
            //else log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "User is NOT authenticated");

            //define the presence of the SIDs in the security Token
                present_groups = Compare(sids, s.IdClient.Groups);
                //or other solution
                //present_groups = CreatePresents(s, sids);
            }
            catch(Exception e)
            {
                s.ProcessException(e);
                return;
            }
            //populate SendTo
            ushort len_presents = (ushort)present_groups.Length;
            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "len_presents {0}", len_presents);

            byte[] sendTo = new byte[2 + present_groups.Length];
            sendTo[0] = (byte)((len_presents >> 8) & 0xff);
            sendTo[1] = (byte)(len_presents & 0xff);
            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, " sendTo[0] {0}, sendTo[1] {1}", sendTo[0], sendTo[1]);
            Array.Copy(present_groups, 0, sendTo, 2, len_presents);

            // send sendTo and call ReuseTheSocketForAnotherRequest
            s.Write(sendTo, Server.ReuseTheSocketForAnotherRequest);
        }
        /// <summary>
        /// Verify if the user is an authenticated userCreate Sids with the bytes sent by tomcat
        /// </summary>
        /// <param name="s">State of the process</param>
        /// <returns>return true if the user is an authenticated user, else false</returns>
        private static bool isUserAuthenticated(State s)
        {
            //test if the user is authenticated
            bool _isUserAuthenticated = false;
            foreach (IdentityReference id in s.IdClient.Groups)
            {
                SecurityIdentifier sid = id as SecurityIdentifier;
                if (!_isUserAuthenticated && sid != null)
                {
                    if (sid.IsWellKnown(WellKnownSidType.AuthenticatedUserSid)) _isUserAuthenticated = true;
                }
            }

            //other solution
            /*
            SecurityIdentifier authenticatedUser = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "Sid Authenticated user {0}", authenticatedUser);
            WindowsPrincipal principal = new WindowsPrincipal(s.IdClient);
            if (principal.IsInRole(authenticatedUser)) log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "Is in Role authenticated");
            else log.TraceEvent(TraceEventType.Verbose, s.ClientPort, " Is not in role authenticated");
            */

            if (_isUserAuthenticated)
            {
                log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "User IS Authenticated");
                return true;
            }
            else
            {
                log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "User is NOT authenticated");
            }
            
            return false;
        }
        /// <summary>
        /// Create Sids with the bytes sent by tomcat
        /// </summary>
        /// <param name="tabsids">array sent by tomcat</param>
        /// <param name="s">State </param>
        /// <returns>array of Sids</returns>
        private static SecurityIdentifier[] CreateSids(byte[] tabsids, State s)
        {
            int nbsids = ValueOfTwoBytes(0, tabsids);
            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "count of recived sids : {0}", nbsids);
            int longueur_sid;
            int position = 2;
            byte[] sid;
            SecurityIdentifier[] secid = new SecurityIdentifier[nbsids];
            for (int i = 0; i < nbsids; i++)
            {
                longueur_sid = ValueOfTwoBytes(position, tabsids);
                log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "sid length : {0}", longueur_sid);
                position += 2;
                if (longueur_sid == 0)
                {
                    secid[i] = null;
                    continue;
                }
                sid = new byte[longueur_sid];
                Array.Copy(tabsids, position, sid, 0, longueur_sid);
                secid[i] = new SecurityIdentifier(sid, 0);
                position += longueur_sid;
            }
            return secid;
        }
        /// <summary>
        /// Could replace <see cref="Compare">Compare</see>. Use principal.isInrole(sid)
        /// </summary>
        /// <param name="s">State</param>
        /// <param name="sids_array">Sids of the tomcat roles</param>
        /// <returns></returns>
        private static byte[] CreatePresents(State s, SecurityIdentifier[] sids_array)
        {
            byte[] present = new byte[sids_array.Length];
            for (int i = 0; i < sids_array.Length; i++) present[i] = 0;

            WindowsPrincipal principal = new WindowsPrincipal(s.IdClient);
            SecurityIdentifier grpSid = null;
            for(int i = 0; i < sids_array.Length; i++)
            {
                grpSid = sids_array[i];
                if (grpSid == null) continue;
                if (principal.IsInRole(grpSid)) present[i] = 1;
            }
            return present;
        }
        /// <summary>
        /// Create an rray of bytes called present. An element is 1, when the same element of sids_array of Sids is present  in sidsCollection , else 0 
        /// </summary>
        /// <param name="sids_array">Sids of the tomcat roles</param>
        /// <param name="sidsCollection">Sids in the access token</param>
        /// <parame name="state">State used to log if the user is or not authenticated</parame>
        /// <returns>return present </returns>
        private static byte[] Compare(SecurityIdentifier[] sids_array, IdentityReferenceCollection sidsCollection)
        {
            byte[] present = new byte[sids_array.Length];
            for (int i = 0; i < sids_array.Length; i++) present[i] = 0;

            //bool isUserAuthenticated = false;
            foreach (IdentityReference id in sidsCollection)
            {               
                SecurityIdentifier ids = id as SecurityIdentifier;
                if (ids != null)
                {
                    //if (!isUserAuthenticated)
                    //{
                    //    if (ids.IsWellKnown(WellKnownSidType.AuthenticatedUserSid)) isUserAuthenticated = true;
                    //}
                    for (int i = 0; i < sids_array.Length; i++)
                    {
                        SecurityIdentifier sidGrp = sids_array[i];
                        if (sidGrp == null)
                        {
                            continue;
                        }
                        if (sidGrp.Equals(ids))
                        {
                            present[i] = 1;
                            break;
                        }
                    }
                }
            }
            //if (isUserAuthenticated) log.TraceEvent(TraceEventType.Verbose, state.ClientPort, "User is authenticated");
            //else log.TraceEvent(TraceEventType.Verbose, state.ClientPort, "User is NOT authenticated");
            return present;
        }
    }
    /// <summary>
    /// Translation of the tomcat roles names into sids 
    /// </summary>
    /// <remarks>
    /// <h2>Call tree:</h2>
    /// <pre>
    ///      Translation
    ///            TranslateAndSend (via ReadAtTheOffset)
    ///              TranslateCharsBigEndianToSids
    ///                TranslateCharsToSids
    ///                  PrepareTheSendResponseToTheWebServer
    ///                Server.ReuseTheSocketForAnotherRequest (via Write)
    /// </pre>
    /// </remarks>
    internal static class TranslateServer
    {
        internal static TraceSource log = new TraceSource("TranslateServer");
        /// <summary>
        /// Read the bytes sent by tomcat, and then <see cref="TranslateAndSend"> TranslateAndSend </see> is invoked 
        /// via <see cref="State.Read" > State.Read </see> and <see cref="State.ReadContinue" > State.ReadContinue </see> 
        /// </summary>
        /// <param name="ts">State ( where is the buffer) </param>
        internal static void Translation(State ts)
        {
            byte[] header = ts.Buffer;  // length == 2
            int size_chars = header[0];
            size_chars = (size_chars << 8) + header[1];

            int size_bytes = sizeof(char) * size_chars;

            byte[] buf = new byte[size_bytes];
            ts.Read(buf, TranslateAndSend);

        }        
        /// <summary>
        /// Take the chars received from tomcat and call <see cref="TranslateCharsBigEndianToSids"> TranslateCharsBigEndianToSids </see>
        /// to translate these bytes in Sids.
        /// Then call <c>Write</c> to send the Sids to tomcat.The callback given to <c>Write</c> is <see cref="Server.ReuseTheSocketForAnotherRequest">Server.ReuseTheSocketForAnotherRequest</see>
        /// </summary>
        /// <param name="s"> State to find the buffer wehe are the bytes sent by tomcat</param>
        internal static void TranslateAndSend(State s)
        {
            /*
            Thread thread = new Thread(TranslateAndSendInAnotherThread);
            thread.Start(s);
             */
            byte[] buf = s.Buffer;
            byte[] toSend = TranslateCharsBigEndianToSids(buf, s);
            if (toSend == null)
            {
                log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "TranslateCharsBigEndianToSids returns null => close the connection with Tomcat");
                s.CleanUp();
            }
            else
            {
                s.Write(toSend, Server.ReuseTheSocketForAnotherRequest);
            }
        }
        /*
        /// <summary>
        /// Take the chars received from tomcat and call <see cref="TranslateCharsBigEndianToSids"> TranslateCharsBigEndianToSids </see>
        /// to translate these bytes in Sids.
        /// Then call <c>BeginWrite</c> to send the Sids to tomcat.The callback given to <c>BeginWrite</c> is <see cref="EndtoSend">EndToSend</see>
        /// </summary>
        /// <param name="state"> State to find the buffer wehe are the bytes sent by tomcat</param>

        internal static void TranslateAndSendInAnotherThread(object state)
        {
            State s = (State)state;

            byte[] buf = s.Buffer;
            byte[] toSend = TranslateCharsBigEndianToSids(buf, s);
            if (toSend == null)
            {
                log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "renvoi null");
                toSend = new byte[2];
                toSend[0] = toSend[1] = 0;
            }
            Stream stream = s.InnerStream;
            try
            {
                s.NewTimer();
                stream.BeginWrite(toSend, 0, toSend.Length, EndtoSend, s);
            }
            catch (Exception e)
            {
                s.ProcessException(e);
            }
        }
        */
        /// <summary>
        /// Called from <see cref="TranslateAndSend">TranslateAndSend</see>
        /// Translate from BIG_ENDIAN to LITTLE_ENDAN, then call <see cref="TranslateCharsToSids"> TranslateCharsToSids </see>
        /// to send the result to <see cref="TranslateAndSend">TranslateAndSend </see>
        /// </summary>
        /// <param name="groups">the bytes array sent by tomcat</param>
        /// <param name="s">State</param>
        /// <returns>Bytes to send to Tomcat received from <see cref="TranslateCharsToSids">TranslateCharsToSids</see> </returns>
        static byte[] TranslateCharsBigEndianToSids(byte[] groups, State s)
        {
            // BigEndianUnicode byte[] -> unicode char[]
            char[] unicode = Encoding.BigEndianUnicode.GetChars(groups);
            return TranslateCharsToSids(unicode, s);
        }
        /*
        static byte[] UnicodeToBigEndianUnicode(char[] unicode)
        {
            // Unicode char[] -> byte[] BigEndianUnicode
            return  Encoding.BigEndianUnicode.GetBytes(unicode);
        }
        */
        /// <summary>
        /// <pre>
        /// Translate an array of characters received from a server Web.
        /// The first element of the array is the size -1 ( size of stream  &lt; 2**16 -1)
        /// The second element  is the count of pairs (ushort)   
        ///  for each pair
        ///  the count of characters of the group's name (ushort)
        ///  the sequence of characters of the group's name (without 0)
        ///</pre>
        ///<c>LsaLookupNmaes2</c> is invoked via <see cref="NTAccount.Translate">NTAccount.Translate</see>
        /// </summary>
        /// <param name="groups">the bytes array given 
        /// by <see cref="TranslateCharsBigEndianToSids">TranslateCharsBigEndianToSids</see>
        /// </param>
        /// <param name="s">State</param>
        /// <returns>return the array returned 
        /// by <see cref="PrepareTheSendResponseToTheWebServer">PrepareTheSendResponseToTheWebServer</see></returns>

        static byte[] TranslateCharsToSids(char[] groups, State s)
        {
            if (groups.Length == 0)
            {
                log.TraceEvent(TraceEventType.Warning, s.ClientPort, "Nothing to read");
                return null; //error
            }

            if ((groups[0] == 0) && (groups.Length  != 1))// if length ==1 and the number of groups == 0 : it's good
            {
                log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "(groupsNumber == 0   and size of the array groups != 1 : size :{0}",
                                        groups.Length);
                return null; //erreur
            }

            uint groups_length = (uint)groups.Length;
            uint groupsNumber = groups[0];

            uint positionOfTheLengthOfTheGroup = 1;
            uint positionOfTheGroup = 2; 
            uint remaining_chars = groups_length - 1;
            uint length_all_sids = 0;
            uint[] length_each_sid = new uint[groupsNumber];

            SecurityIdentifier[] security_identifiers = new SecurityIdentifier[groupsNumber];
            for (uint i = 0; i < groupsNumber; i++)
            {
                if (positionOfTheLengthOfTheGroup > groups_length) // so position is an index of range of the array groups
                {
                    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "positionOfTheLengthOfTheGroup > groups_length: position {0}, groups_length {1}",
                        positionOfTheLengthOfTheGroup, groups_length);
                    return null; //erreur à gérer
                }

                uint sizeOfTheGgroup = groups[positionOfTheLengthOfTheGroup];
                remaining_chars--;
                positionOfTheGroup = positionOfTheLengthOfTheGroup + 1;
                if (sizeOfTheGgroup > remaining_chars)
                {
                    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "iteration {0} : (sizeOfTheGgroup > remaining_chars): sizeOfTheGgroup : {1}, remaining_chars: {2} ",
                        i, sizeOfTheGgroup, remaining_chars);
                    return null; //error
                }

                String Name_group = null;
                SecurityIdentifier si = null;
                length_each_sid[i] = 0;
                if (sizeOfTheGgroup > 0)
                {
                    try
                    {
                        //translation of a group name into a SID
                        Name_group = new String(groups, (int)positionOfTheGroup, (int)sizeOfTheGgroup);
                        NTAccount temp = new NTAccount(Name_group);
                        si = (SecurityIdentifier)temp.Translate(typeof(SecurityIdentifier));
                        length_each_sid[i] = (uint)si.BinaryLength;
                    }
                    catch (Exception e)
                    {
                        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "Exception : " + e.Message + " " + e.StackTrace);
                        si = null;
                    }
                }
                length_all_sids += length_each_sid[i];
                security_identifiers[i] = si;
                positionOfTheLengthOfTheGroup += (sizeOfTheGgroup + 1);
                remaining_chars -= sizeOfTheGgroup;

                if (si != null) log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "Name : {0} , sid : {1}", Name_group, si.ToString());
            }
            if (remaining_chars != 0)
            {
                return null; //erreur
            }
            return PrepareTheSendResponseToTheWebServer(length_all_sids, length_each_sid, groupsNumber, security_identifiers);
        }

        /// <summary>
        /// <pre>
        ///Prepare an array of bytes to send : 
        ///  Two first bytes = length of the array (ushort BIG_ENDIAN)
        ///  two next bytes  = count of pairs (unshort BIG_ENDIAN)
        ///  Each pair is :
        ///     size of sid (ushort BIG_ENDIAN)
        ///     sequence of bytes
        /// </pre>
        /// </summary>
        /// <param name="lengthOfAllSids">Sum of the lengths of each sid  </param>
        /// <param name="lengthOfEachSid">array with the length of each Sid</param>
        /// <param name="sidsNumber">Count of Sids</param>
        /// <param name="security_identifiers">Array of Sids created in <see cref="TranslateCharsToSids"/> </param>
        /// <returns>The bytes array to send to Tomcat</returns>
        static byte[] PrepareTheSendResponseToTheWebServer( uint lengthOfAllSids, uint[] lengthOfEachSid, 
                            uint sidsNumber, SecurityIdentifier[] security_identifiers)
        {
            uint nbHeaders = 2; // size to send  + count of SIDs
            uint sizeofTabOfSidsWithHeaders = lengthOfAllSids + (nbHeaders + sidsNumber) * sizeof(ushort);
            uint size_tab_sids_whithout_header_size = (uint)(sizeofTabOfSidsWithHeaders - sizeof(ushort));
            byte[] tabOfSidsWithHeaders = new byte[sizeofTabOfSidsWithHeaders];

            //array length (BIG_ENDIAN)
            tabOfSidsWithHeaders[0] = (byte)(size_tab_sids_whithout_header_size >> 8);
            tabOfSidsWithHeaders[1] = (byte)(size_tab_sids_whithout_header_size & 0xff);

            if (lengthOfAllSids != 0 )// count of sids != 0
            {
                //count of sids (BIG_ENDIAN) : there is at least one group
                tabOfSidsWithHeaders[2] = (byte)(sidsNumber >> 8);
                tabOfSidsWithHeaders[3] = (byte)(sidsNumber & 0xff);

                uint position_tab_sids = 4;
                for (uint i = 0; i < sidsNumber; i++)
                {
                    //length of the sid
                    uint sid_length = lengthOfEachSid[i];
                    tabOfSidsWithHeaders[position_tab_sids++] = (byte)(sid_length >> 8);
                    tabOfSidsWithHeaders[position_tab_sids++] = (byte)(sid_length & 0xff);
                    if (sid_length > 0)
                    {
                        //copy of the sid
                        security_identifiers[i].GetBinaryForm(tabOfSidsWithHeaders, (int)position_tab_sids);
                        position_tab_sids += sid_length;
                    }
                }//for
            }//if
            return tabOfSidsWithHeaders;
        }

    }
    /// <summary>
    /// Class used to authenticate the user with login/password
    /// </summary>
    internal class AuthenticatingWithLogin
    {
        internal static TraceSource log = new TraceSource("AuthenticatingWithLogin");
        public static SyncStack syncstack;
        /// <summary>
        /// Called by <c>Server.StartServer</c> <see cref="Server.StartServer"/>
        /// Two MemoryStreams are pooled in a stack 
        /// <param name="numberOfThreads"> is used to define the number of the Tcp created connexions and put in a pool </param>
        /// <param name="factor">is used to define the number of TwoStreams created .and put in a pool</param>
        /// numberOfThreads * factor = number of tcp connections created in the pool. 
        /// This number can be growing <see cref="SyncStack.Pop"/>SyncStack.Pop
        /// </summary>
        public static void CreateStreamsToAuthenticateAndStackOfTheseStreamsWithBuffers(int numberOfThreads, int factor)
        {
            int countOfConnections = numberOfThreads * factor;
            Stack<TwoStream> stack = new Stack<TwoStream>(countOfConnections);
            TwoBuffersStreams tbs;
            FullDuplexBuffersStream client;
            FullDuplexBuffersStream server;
            for (int i = 0; i < countOfConnections; i++)
            {
                tbs = new TwoBuffersStreams();
                client = new FullDuplexBuffersStream(tbs, ClientOrServer.Client);
                server = new FullDuplexBuffersStream(tbs, ClientOrServer.Server);
                stack.Push(new TwoStream(client, server));
            }
            //syncstack = new SyncStackBuffers(stack);
            syncstack = new SyncStack(stack);
        }
        public static void AuthenticateClientWithLogin(State state)
        {
            //To get the name, the domain and the password of the user
            //1) the length of the request in short (UNICODE BIG ENDIAN)
            //2) the length of the name in short
            //3) the name in char[] UNICODE Big Endian
            //4) the length of the domain in short
            //5) the domain in char[] UNICODE Big Endian
            //6) the length of the password in short
            //7) the password in char[] UNICODE Big Endian

            byte[] header = state.Buffer;  // length == 2
            //BIG ENDIAN
            int size_chars = header[0];
            size_chars = (size_chars << 8) + header[1];
            if (size_chars > short.MaxValue)
            {
                log.TraceEvent(TraceEventType.Verbose, state.ClientPort, "size of the login > {0}", short.MaxValue);
                state.CleanUp();
                return; //error
            }
            int size_bytes = sizeof(char) * size_chars;
            log.TraceEvent(TraceEventType.Verbose, state.ClientPort, "count of chars: {0}, count of bytes {1}", size_chars, size_bytes);
            byte[] buf = new byte[size_bytes];
            state.Read(buf, GetLoginDomainPasswordInBigEndian);
        }
        public static void GetLoginDomainPasswordInBigEndian(State state)
        {
            byte[] b = state.Buffer;
            char[] unicode = Encoding.BigEndianUnicode.GetChars(b);
            GetLoginPassword(state, unicode);
        }
        public static void GetLoginPassword(State s, char[] credentials)
        {
            //1) the length of the name in short        (lengthOfName)      must be >0
            //2) the name in char[] UNICODE Little Endian
            //3) the length of the domain in short      (lengthOfDomain)    can be 0
            //4) the domain in char[] UNICODE Little Endian                 can be void
            //5) the length of the password in short    (lengthOfPassword)  must be > 0
            //6) the password in char[] UNICODE Little Endian

            int toRead = credentials.Length;
            int position = 0;
            int remaining = toRead;

            //Nothing
            if (credentials.Length < 1)
            {
                log.TraceEvent(TraceEventType.Warning, s.ClientPort, "Nothing to read");
                s.CleanUp();
                return;
            }

            // name
            int lengthOfName = credentials[position];
            position++;
            remaining--;

            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "length name of the user: {0}, Count of chars to read: {1}", lengthOfName, remaining);
            if ( (remaining <= 0) || (lengthOfName <= 0) || (lengthOfName >= remaining) )
            {
                s.CleanUp();
                return; //error
            }
            String name = new String(credentials, position, lengthOfName);
            position += lengthOfName;
            remaining -= lengthOfName;

            // domain
            int lengthOfDomain = credentials[position];
            position++;
            remaining--;
            
            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "length of the domain: {0}, Count of chars to read: {1}", lengthOfDomain, remaining);
            //Length of the Domain can be zero
            if ( (remaining <= 0) || (lengthOfDomain < 0) || (lengthOfDomain >= remaining) )
            {
                s.CleanUp();
                return; //error
            }
            String domain = "";
            if (lengthOfDomain != 0)
            {
                domain = new String(credentials, position, lengthOfDomain);
                position += lengthOfDomain;
                remaining -= lengthOfDomain;
            }

            // password
            int lengthOfPassword = credentials[position];
            position++;
            remaining--;

            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "length of the password: {0}, Count of chars to read: {1}", lengthOfPassword, remaining);
            if ( (remaining <= 0) || (lengthOfPassword <= 0) || (lengthOfPassword != remaining) )
            {
                s.CleanUp();
                return; //error
            }
            if (lengthOfPassword > byte.MaxValue +1)
            {
                log.TraceEvent(TraceEventType.Verbose, s.ClientPort, " size of the password > {0}", byte.MaxValue +1);
                s.CleanUp();
                return;//error
            }
            String password = new String(credentials, position, lengthOfPassword);

            AuthenticateClientWithLoginAndNegotiaStream2(s, name, domain, password);
        }
        //public static void AuthenticateClientWithLoginAndNegotiaStream(State s, String userName, String domain, String password)
        //{
        //    NetworkCredential cred;
        //    if (domain != "")
        //    {
        //        cred = new NetworkCredential(userName, password, domain);
        //    }
        //    else
        //    {
        //        int index = userName.IndexOf('\\');
        //        if (index != -1)
        //        {
        //            domain = userName.Substring(0, index );
        //            userName = userName.Substring(index + 1);
        //            cred = new NetworkCredential(userName, password, domain);
        //        }
        //        else cred = new NetworkCredential(userName, password);
        //    }

        //    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "AuthenticateClientWithLogin: userName = {0}, domain = {1}, password = {2}", userName, domain, "XXXX");

        //    TwoStream two = null;
        //    try
        //    {
        //        two = syncstack.Pop();
        //    }
        //    catch (Exception e)
        //    {
        //        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, e.GetType() + " : " + e.Message + ", The user cannot be authenticated: No stream to authenticate the user!!!");
        //        s.CleanUp();
        //        return;
        //    }
        //    two.Client.InitOrReinit(s.ClientPort);
        //    two.Server.ClientPort = s.ClientPort;

        //    NegotiateStream clientside = new NegotiateStream(two.Client, true);
        //    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "ThreadId: Before BeginAuthenticateAsClient " + Thread.CurrentThread.ManagedThreadId);
        //    try
        //    {
        //        clientside.BeginAuthenticateAsClient(cred, "", ProtectionLevel.EncryptAndSign, TokenImpersonationLevel.Identification,
        //            delegate(IAsyncResult iar)
        //            {
        //                //Now there is nothing on the wire
        //                NegotiateStream nego = null;
        //                try
        //                {
        //                    nego = (NegotiateStream)iar.AsyncState;
        //                    nego.EndAuthenticateAsClient(iar);
        //                }
        //                catch (Exception e)
        //                {
        //                    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, e.GetType() + " LoginAuthentication is Failed (EndAuthenticateAsClient) " + e.Message);
        //                }
        //                finally
        //                {
        //                    syncstack.Push(two);
        //                    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "ThreadId: After EndAuthenticateAsClient " + Thread.CurrentThread.ManagedThreadId);

        //                }
        //                try
        //                {
        //                    nego.Close();
        //                    log.TraceEvent(TraceEventType.Verbose, s.ClientPort," NegotiateStream client used with login is closed ");
        //                }
        //                catch(Exception e)
        //                {
        //                    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, e.GetType() + " NegotiateStream client cannot be closed " + e.Message);
        //                }
        //            }, clientside);
        //    }
        //    catch (Exception ex)
        //    {
        //        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, ex.GetType() + " LoginAuthentication is Failed (BeginAUthenticationAsClient) " + ex.Message);
        //        if (ex.InnerException != null)
        //            log.TraceEvent(TraceEventType.Verbose, s.ClientPort,"Inner Exception: " +  ex.InnerException.GetType() + ex.InnerException.Message);
        //        syncstack.Push(two);
        //        s.CleanUp();
        //        return;
        //    }
        //    s.AuthenticateAsServerLogin(AsynchronousAuthenticatingTcpListener.SendNameAndSSP, two.Server);
        //}
        public static void AuthenticateClientWithLoginAndNegotiaStream2(State s, String userName, String domain, String password)
        {
            NetworkCredential cred;
            
            if (domain != "")
            {
                cred = new NetworkCredential(userName, password, domain);
            }
            else
            {
                int index = userName.IndexOf('\\');
                if (index != -1)
                {
                    domain = userName.Substring(0, index);
                    userName = userName.Substring(index + 1);
                    cred = new NetworkCredential(userName, password, domain);
                }
                else cred = new NetworkCredential(userName, password);
            }

            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "AuthenticateClientWithLogin: userName = {0}, domain = {1}, password = {2}", userName, domain, "XXXX");

            TwoStream two = null;
            try
            {
                two = syncstack.Pop();
            }
            catch (Exception e)
            {
                log.TraceEvent(TraceEventType.Verbose, s.ClientPort, e.GetType() + " : " + e.Message + ", The user cannot be authenticated: No stream to authenticate the user!!!");
                s.CleanUp();
                return;
            }
            two.Client.InitOrReinit(s.ClientPort);
            two.Server.ClientPort = s.ClientPort;

            NegotiateStream clientside = new NegotiateStream(two.Client, true);
            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "ThreadId: Before AuthenticateAsClient " + Thread.CurrentThread.ManagedThreadId);
            ThreadPool.QueueUserWorkItem(delegate
                {
                    try
                    {
                        clientside.AuthenticateAsClient(cred, "", ProtectionLevel.EncryptAndSign, TokenImpersonationLevel.Identification);
                    }
                    catch (Exception e)
                    {
                        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, e.GetType() + " LoginAuthentication is Failed (EndAuthenticateAsClient) " + e.Message);
                        if (e.InnerException != null)
                            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "Inner Exception: " + e.InnerException.GetType() + e.InnerException.Message);
                    }
                    finally
                    {
                        syncstack.Push(two);
                        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "ThreadId: After EndAuthenticateAsClient " + Thread.CurrentThread.ManagedThreadId);
                    }
                    try
                    {
                        clientside.Close();
                        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, " NegotiateStream client used with login is closed ");
                    }
                    catch (Exception e)
                    {
                        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, e.GetType() + " NegotiateStream client cannot be closed " + e.Message);
                    }
                });
            s.AuthenticateAsServerLogin2(AsynchronousAuthenticatingTcpListener.SendNameAndSSP, two.Server);

        }
        //public static void AuthenticateClientWithLoginAndNegotiaStream3(State s, String userName, String domain, String password)
        //{
        //    NetworkCredential cred;

        //    if (domain != "")
        //    {
        //        cred = new NetworkCredential(userName, password, domain);
        //    }
        //    else
        //    {
        //        int index = userName.IndexOf('\\');
        //        if (index != -1)
        //        {
        //            domain = userName.Substring(0, index);
        //            userName = userName.Substring(index + 1);
        //            cred = new NetworkCredential(userName, password, domain);
        //        }
        //        else cred = new NetworkCredential(userName, password);
        //    }

        //    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "AuthenticateClientWithLogin: userName = {0}, domain = {1}, password = {2}", userName, domain, "XXXX");

        //    TwoStream two = null;
        //    try
        //    {
        //        two = syncstack.Pop();
        //        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "Syncstack Pop");
        //    }
        //    catch (Exception e)
        //    {
        //        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, e.GetType() + " : " + e.Message + ", The user cannot be authenticated: No stream to authenticate the user!!!");
        //        s.CleanUp();
        //        return;
        //    }
        //    two.Client.InitOrReinit(s.ClientPort);
        //    two.Server.ClientPort = s.ClientPort;

        //    NegotiateStream clientside = new NegotiateStream(two.Client, true);
        //    log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "ThreadId: Before BeginAuthenticateAsClient " + Thread.CurrentThread.ManagedThreadId);
        //    s.AuthenticateAsServerLogin2(AsynchronousAuthenticatingTcpListener.SendNameAndSSP, two.Server);
        //    try
        //    {
        //        clientside.AuthenticateAsClient(cred, "", ProtectionLevel.EncryptAndSign, TokenImpersonationLevel.Identification);
        //    }
        //    catch (Exception e)
        //    {
        //        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, e.GetType() + " LoginAuthentication is Failed (EndAuthenticateAsClient) " + e.Message);
        //        if (e.InnerException != null)
        //            log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "Inner Exception: " + e.InnerException.GetType() + e.InnerException.Message);
        //    }
        //    finally
        //    {
        //        syncstack.Push(two);
        //        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, "SyncStack Push ThreadId: After EndAuthenticateAsClient " + Thread.CurrentThread.ManagedThreadId);
        //    }
        //    try
        //    {
        //        clientside.Close();
        //        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, " NegotiateStream client used with login is closed ");
        //    }
        //    catch (Exception e)
        //    {
        //        log.TraceEvent(TraceEventType.Verbose, s.ClientPort, e.GetType() + " NegotiateStream client cannot be closed " + e.Message);
        //    }


        //}
    }
    /// <summary>
    /// created in <see cref="Server.StartServer">Server.StartServer</see> after <c>AcceptTcpClient</c> and 
    /// used by <see cref="Server.ProcessClientRequest">Server.ProcessClientRequest</see>
    /// </summary>
    internal class RequestState
    {
        private TcpClient client;
        private Parameters param;
        private int clientport;
        internal RequestState(TcpClient c, Parameters p)
        {
            client = c;
            param = p;
            IPEndPoint ep = (IPEndPoint)(c.Client.RemoteEndPoint);
            clientport = ep.Port;
        }
        internal TcpClient Client { get { return client; } }
        internal Parameters Param { get { return param; } }
        internal int ClientPort { get { return clientport; } }
    }
    /// <summary>
    /// Delegate to call at the end of <see cref="State.ReadContinue"> State.ReadContinue </see>
    /// </summary>
    /// <param name="s">State used by the method called</param>
    internal delegate void NextToDo(State s);
    /// <summary>
    /// Used to simulate a reading loop asynchronously, to cleanup, to define a time-out for reading and writing and to 
    /// get and set the WindowsIdentity of the client.
    /// </summary>
    internal class State
    {
        internal static TraceSource log = new TraceSource("State");
        private TcpClient tcpclient = null;
        private int clientport = 0;
        private NextToDo todo = null;

        private byte[] buffer = null;
        private int offset = 0;
        private int remaining = 0;
        //private int length_buf = 0;

        private NegotiateStream authStream = null;
        private Stream innerstream = null;
        private bool iskerberosonly = false;
        private bool iswithlogin = false;

        private bool mustInformIfAuthenticatedUser = false;
        /// <summary>
        /// Timer to define a Time-out
        /// <c>new Timer(new TimerCallback(this.CancelConnection));</c>
        /// </summary>
        private Timer t = null; 
        WindowsIdentity idclient = null;
        internal State(Stream stream, TcpClient client, bool kerberosonly, bool withlogin)
        {
            innerstream = stream;
            IPEndPoint ipe = client.Client.RemoteEndPoint as IPEndPoint;
            clientport = ipe.Port;
            iskerberosonly = kerberosonly;
            iswithlogin = withlogin;
        }
        /// <summary>
        /// Inform or not tomcat if the user is memeber of the group Authenticated Users
        /// </summary>
        internal bool MustInformIfAuthenticatedUser
        {
            get { return mustInformIfAuthenticatedUser; }
            set { mustInformIfAuthenticatedUser = value; }
        }
        /// <summary>
        /// Accept or not the authentication via login/password
        /// </summary>
        internal bool IsWithLogin()
        {
            return iswithlogin;
        }
        /// <summary>
        /// bytes array used by <see cref="Read">Read </see> and <see cref="ReadContinue"> ReadContinue </see>
        /// </summary>
        internal byte[] Buffer
        {
            get { return buffer; }
        }
        /// <summary>
        /// TcpClient
        /// </summary>
        internal TcpClient Client
        {
            get { return tcpclient; }
        }
        /// <summary>
        /// stream (NetworkStream or SslStream
        /// </summary>
        internal Stream InnerStream
        {
            get { return innerstream; }
        }
        /// <summary>
        /// What call after <see cref="ReadContinue">ReadContinue</see>
        /// </summary>
        internal NextToDo ToDo
        {
            get { return todo; }
        }
        /// <summary>
        /// Access Token of the client (Impersonation level Identity)
        /// </summary>
        internal WindowsIdentity IdClient
        {
            get { return idclient; }
        }
        internal int ClientPort
        {
            get { return clientport; }
        }
        /// <summary>
        /// Stop the timer, so the timer is disabled, 
        /// <see cref="CleanUp">CleanUp</see> and 
        /// <see cref="ReadContinue">ReadContinue</see> and 
        /// <see cref="WriteContinue">WriteContinue</see> and        
        /// <see cref="AuthenticateAsServerContinue">AuthenticateAsServerContinue</see> 
        /// </summary>
        private void StopTimer()
        {
            if (t != null)
            {
                try
                {
                    t.Dispose();
                }
                catch (Exception e)
                {
                    log.TraceEvent(TraceEventType.Verbose, clientport, e.GetType() + ": " + e.Message);
                }
                t = null;
            }
        }
        private void CancelConnection(object o)
        {
            CleanUp();
        }
        internal void ProcessException(Exception e)
        {
            log.TraceEvent(TraceEventType.Verbose, ClientPort, e.Message + " " + e.StackTrace);
            CleanUp();
        }
        internal void CleanUp()
        {
            StopTimer();
            if (idclient != null)
            {
                try
                {
                    idclient.Dispose();
                }
                catch (Exception e)
                {
                    log.TraceEvent(TraceEventType.Verbose, ClientPort, e.GetType().FullName + " : " + e.Message + " " + e.StackTrace);
                }
                idclient = null;
            }
            
            CleanUpAuthStream();

            if (InnerStream != null)
            {
                try
                {
                    innerstream.Close();
                    if (tcpclient != null) tcpclient.Close();
                }
                catch (Exception e)
                {
                    log.TraceEvent(TraceEventType.Verbose, ClientPort, e.GetType().FullName + " : " + e.Message + " " + e.StackTrace);
                }
                innerstream = null;
                tcpclient = null;
                log.TraceEvent(TraceEventType.Verbose, ClientPort, "Connection closed");
                log.Flush();
            }
        }
        /// <summary>
        /// Like<see cref="Read"/>, but the time-out is 6 minutes.
        /// </summary>
        internal void WaitTheNextRequest( NextToDo todo)
        {
            try
            {
                if (idclient != null)
                {
                    idclient.Dispose();
                    idclient = null;
                }
                t = new Timer(new TimerCallback(this.CancelConnection));
                t.Change(1000 * 60 * 6, Timeout.Infinite); //timeout 6 minutes AsyncReceiveTimeout (So this connection can be reused cf java)
                log.TraceEvent(TraceEventType.Verbose, ClientPort, "Before BeginRead");
                
                buffer = new byte[1];
                offset = 0;
                remaining = 1;
                this.todo = todo;

                innerstream.BeginRead(Buffer, offset, remaining, this.ReadContinue, this);
            }
            catch (Exception e)
            {
                ProcessException(e);
            }
            log.TraceEvent(TraceEventType.Verbose, ClientPort, "WaitTheNextRequest launched");
        }
        /// <summary>
        /// Read and ReadContinue replace a loop.
        /// Instances variables replace the local variables
        /// The action after the loop is defined via NextToDo.
        /// The time-out is 9 seconds.
        /// </summary>
        /// 
        internal void Read(byte[] buf, NextToDo todo)
        {
            buffer = buf;
            offset = 0;
            remaining = buffer.Length;
            this.todo = todo;

            log.TraceEvent(TraceEventType.Verbose, ClientPort, "Buf len : {0}", Buffer.Length);
            log.TraceEvent(TraceEventType.Verbose, ClientPort, "Buf remaining {0}", remaining);
            try
            {
                t = new Timer(new TimerCallback(this.CancelConnection));
                t.Change(1000 * 9, Timeout.Infinite); //timeout 9 seconds:  AsyncReceiveTimeout
                innerstream.BeginRead(Buffer, 0, remaining, this.ReadContinue, this);
            }
            catch (Exception e)
            {
                ProcessException(e);
            }
        }
        private void ReadContinue(IAsyncResult iar)
        {
            //if stream is closed(timer or something else) end the request (=> return)
            if (innerstream == null) return;

            Stream stream = innerstream;
            int chunk_size = 0;
            try
            {
                if (stream == null) return;
                chunk_size = stream.EndRead(iar);
            }
            catch (Exception e)
            {
                ProcessException(e);
                return;
            }
            remaining -= chunk_size;
            offset += chunk_size;
            if (chunk_size > 0 && remaining > 0)
            {
                try
                {
                    stream.BeginRead(Buffer, offset, remaining, this.ReadContinue, this);
                }
                catch (Exception e)
                {
                    ProcessException(e);
                    return;
                }

            }
            else if (remaining == 0)
            {
                StopTimer();
                NextToDo todo = ToDo;
                if (todo != null)todo(this);
            }
            else
            {
                if (chunk_size == 0)
                {
                    log.TraceEvent(TraceEventType.Verbose, ClientPort, "The connection has been closed by the other side");
                }
                else
                {
                    log.TraceEvent(TraceEventType.Verbose, ClientPort, "Count of Bytes Sent by the web server is not equal to the Count of Bytes in the headers");
                }
                CleanUp();
            }
        }
        /// <summary>
        /// Read and ReadContinue replace a loop.
        /// Instances variables replace the local variables
        /// The action after the loop is defined via NextToDo.
        /// The time-out is 9 seconds.
        /// </summary>
        /// 
        internal void Write(byte[] buf, NextToDo todo)
        {
            log.TraceEvent(TraceEventType.Verbose, ClientPort, "Buf len : {0}", buf.Length);
            this.todo = todo;
            try
            {
                t = new Timer(new TimerCallback(this.CancelConnection));
                t.Change(1000 * 9, Timeout.Infinite); //timeout 9 seconds:  AsyncReceiveTimeout
                innerstream.BeginWrite(buf, 0, buf.Length, this.WriteContinue, this);
            }
            catch (Exception e)
            {
                ProcessException(e);
            }
        }
        private void WriteContinue(IAsyncResult iar)
        {
            log.TraceEvent(TraceEventType.Verbose, clientport, "In WriteContinue");
            Stream stream = innerstream;
            try
            {
                stream.EndWrite(iar);
            }
            catch (Exception e)
            {
                ProcessException(e);
                return;
            }
            StopTimer();
            log.TraceEvent(TraceEventType.Verbose, clientport, "Before to call NextToDo");
            NextToDo todo = ToDo;
            if(todo != null) todo(this);
        }
        internal void AuthenticateAsServer(NextToDo todo)
        {
            this.todo = todo;
            authStream = new NegotiateStream(innerstream, true); // leaveInnerStreamOpen == true

            ProtectionLevel protection = ProtectionLevel.None;//can be used with NTLM without SPNEGO
            if (iskerberosonly) protection = ProtectionLevel.EncryptAndSign;
            try
            {
                t = new Timer(new TimerCallback(this.CancelConnection));
                t.Change(1000 * 20, Timeout.Infinite); //timeout 20 seconds AsyncReceiveTimeout (So this connection can be reused cf java)

                authStream.BeginAuthenticateAsServer(
                    CredentialCache.DefaultNetworkCredentials,
                    protection,
                    TokenImpersonationLevel.Impersonation,
                    new AsyncCallback(this.AuthenticateAsServerContinue), null);
            }
            catch (Exception e)
            {
                ProcessException(e);
            }
        }
        private void AuthenticateAsServerContinue(IAsyncResult ar)
        {
            // Any exceptions that occurred during authentication are
            // thrown by the EndAuthenticateAsServer method.
            try
            {
                StopTimer();
                authStream.EndAuthenticateAsServer(ar);
                if (authStream.IsAuthenticated)
                {
                    idclient = (WindowsIdentity)authStream.RemoteIdentity;
                    logAuthenticate();
                }
            }
            catch (Exception e)
            {
                ProcessException(e);
                return;
            }
            CleanUpAuthStream();
            NextToDo todo = ToDo;
            if (todo != null) todo(this);
        }
        internal void AuthenticateAsServerLogin2(NextToDo todo, Stream stream)
        {
            log.TraceEvent(TraceEventType.Verbose, clientport, "ThreadId Before AuthenticateAsServer : " + Thread.CurrentThread.ManagedThreadId);
            this.todo = todo;
            authStream = new NegotiateStream(stream, true);
            ProtectionLevel protection = ProtectionLevel.EncryptAndSign;
            ThreadPool.QueueUserWorkItem(delegate
            {
                try
                {
                    authStream.AuthenticateAsServer(CredentialCache.DefaultNetworkCredentials,
                                                    protection,
                                                    TokenImpersonationLevel.Identification);
                    if (authStream.IsAuthenticated)
                    {
                        idclient = (WindowsIdentity)authStream.RemoteIdentity;
                        logAuthenticate();
                    }
                    else
                    {
                        CleanUp();
                        return;
                    }

                }
                catch (Exception e)
                {
                    ProcessException(e);
                    return;
                }
                CleanUpAuthStream();
                NextToDo todoNow = ToDo;
                if (todoNow != null) todoNow(this);
            });
        }
        //internal void AuthenticateAsServerLogin(NextToDo todo, Stream stream)
        //{
        //    log.TraceEvent(TraceEventType.Verbose, clientport, "ThreadId Before BeginAuthenticateAsServer : " + Thread.CurrentThread.ManagedThreadId);
        //    this.todo = todo;
        //    authStream = new NegotiateStream(stream, true);
        //    ProtectionLevel protection = ProtectionLevel.None;
        //    try
        //    {
        //        authStream.BeginAuthenticateAsServer(
        //            CredentialCache.DefaultNetworkCredentials,
        //            protection,
        //            TokenImpersonationLevel.Identification,
        //            new AsyncCallback(this.AuthenticateAsServerLoginContinue), null);
        //    }
        //    catch (Exception e)
        //    {
        //        ProcessException(e);
        //    }
        //}
        //private void AuthenticateAsServerLoginContinue(IAsyncResult ar)
        //{
        //    // Any exceptions that occurred during authentication are
        //    // thrown by the EndAuthenticateAsServer method.
        //    try
        //    {
        //        authStream.EndAuthenticateAsServer(ar);
        //        if (authStream.IsAuthenticated)
        //        {
        //            idclient = (WindowsIdentity)authStream.RemoteIdentity;
        //            logAuthenticate();
        //        }
        //        else
        //        {
        //            CleanUp();
        //            return;
        //        }
        //    }
        //    catch (Exception e)
        //    {
        //        ProcessException(e);
        //        return;
        //    }
        //    CleanUpAuthStream();
        //    NextToDo todo = ToDo;
        //    if (todo != null) todo(this);
        //}
        private void logAuthenticate()
        {
            string CryptOrSign = "CryptOrSign : ";
            if (authStream.IsEncrypted)
            {
                CryptOrSign += "encrypt ";
            }
            if (authStream.IsSigned)
            {
                CryptOrSign += "signed ";
            }
            if (authStream.IsMutuallyAuthenticated)
            {
                CryptOrSign += "mutually authenticated";
            }
            log.TraceEvent(TraceEventType.Verbose, ClientPort, CryptOrSign);
            log.TraceEvent(TraceEventType.Verbose, clientport, "ThreadId after EndAuthenticateAsServer : " + Thread.CurrentThread.ManagedThreadId);


        }
        private void CleanUpAuthStream()
        {
            if (authStream != null)
            {
                try
                {
                    authStream.Close();
                    log.TraceEvent(TraceEventType.Verbose, ClientPort, " Authstream closed");
                }
                catch (Exception e)
                {
                    log.TraceEvent(TraceEventType.Verbose, ClientPort, e.Message + " " + e.StackTrace);
                }
                authStream = null;
            }
        }
    }   
    internal class TwoStream
    {
        FullDuplexBuffersStream c;
        FullDuplexBuffersStream s;
        public TwoStream(FullDuplexBuffersStream client, FullDuplexBuffersStream server)
        {
            c = client;
            s = server;
        }
        public FullDuplexBuffersStream Client
        {
            get { return c; }
        }
        public FullDuplexBuffersStream Server
        {
            get { return s; }
        }
    }
    internal class SyncStack
    {   
        private Stack<TwoStream> stack;
        private Object sync = new Object(); // used only to pop from and push on the stack: The time of the lock is very small.
        public SyncStack(Stack<TwoStream> s)
        {
            stack = s;
        }
        public void Push(TwoStream two)
        {
            lock (sync)
            {
                stack.Push(two);
            }
        }
        public TwoStream Pop()
        {
            lock (sync)
            {
                if (stack.Count != 0)
                    return stack.Pop();
            }

            TwoStream two = CreateTwoStream();
            return two;
        }
        private object syncbuffers = new object();
        public TwoStream CreateTwoStream()
        {
            //return CreateTwoStreamImpl();
            //Stream client;
            //Stream server;
            FullDuplexBuffersStream client;
            FullDuplexBuffersStream server;
            lock (syncbuffers)
            {
                TwoBuffersStreams tbs = new TwoBuffersStreams();
                client = new FullDuplexBuffersStream(tbs, ClientOrServer.Client);
                server = new FullDuplexBuffersStream(tbs, ClientOrServer.Server);
            }
            return new TwoStream(client, server);

        }
    }
    internal enum ClientOrServer
    {
        Client,
        Server
    }
    internal class TwoBuffersStreams
    {
        public MemoryStreamWithPosition client2Server = new MemoryStreamWithPosition();
        public MemoryStreamWithPosition server2Client = new MemoryStreamWithPosition();
    }
    internal class MemoryStreamWithPosition : MemoryStream
    {
        public int Position_Read { get; set; }
        public int Position_Write { get; set; }
        public object synch = new object();
        public void InitOrReinit()
        {
            Position_Write = Position_Read = 0;
            Position = 0;
            SetLength(0);
        }
    }
    //internal delegate int readDelegate(byte[] buf, int offset, int length);
    //internal delegate void writeDelegate(byte[] buf, int offset, int length);
    internal class FullDuplexBuffersStream : Stream
    {
        internal static TraceSource log = new TraceSource("FullDuplexBuffersStream");
        private TwoBuffersStreams b;
        ClientOrServer cors;
        int clientPort;
        //readDelegate read;
        //writeDelegate write;
        public FullDuplexBuffersStream(TwoBuffersStreams bufs, ClientOrServer cs)
        {
            b = bufs;
            cors = cs;
            //read = this.Read;
            //write = this.Write;
        }
        public void InitOrReinit( int clientport)
        {
            clientPort = clientport;
            b.client2Server.InitOrReinit();
            b.server2Client.InitOrReinit();
            log.TraceEvent(TraceEventType.Verbose, 0, "Reinit the positions of the MemoryStreamsWithPosition");
        }
        public int ClientPort
        {
            set { clientPort = value; }            
        }
        public override bool CanRead
        {
            get { return true; }
        }
        public override bool CanSeek
        {
            get { return false; }
        }
        public override bool CanWrite
        {
            get { return true; }
        }
        public override void Flush()
        {
        }
        public override long Length
        {
            get { throw new NotSupportedException(); }
        }
        public override long Position
        {
            get
            {
                throw new NotSupportedException();
            }
            set
            {
                throw new NotSupportedException();
            }
        }
        //public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        //{
        //    //return read.BeginInvoke(buffer, offset, count, callback, state);
        //    return new FullDuplexAsyncResult<int>(
        //        delegate { return Read(buffer, offset, count); },
        //        callback,
        //        state);
        //}
        //public override int EndRead(IAsyncResult asyncResult)
        //{
        //    //return read.EndInvoke(asyncResult);
        //    FullDuplexAsyncResult<int> fdasyncresult = asyncResult as FullDuplexAsyncResult<int>;
        //    if (fdasyncresult == null) throw new ArgumentException("Bad Async result");
        //    return fdasyncresult.End();
        //}
        public override int Read(byte[] buffer, int offset, int count)
        {
            int chunksize;
            MemoryStreamWithPosition s;

            if (cors == ClientOrServer.Client) s = b.server2Client;
            else s = b.client2Server;

            object l = s.synch;
            lock (l)
            {
                log.TraceEvent(TraceEventType.Verbose, clientPort, "Capacity of the memory Stream before Read {0} : {1}", cors, s.Capacity);
                log.TraceEvent(TraceEventType.Verbose, clientPort, " mswp: {0} Read : count {3}  Position_Read {1}, Position_Write {2}", cors, s.Position_Read, s.Position_Write, count);
                //if nothing to read => wait
                while (s.Position_Read == s.Position_Write)
                {
                    s.Position_Write = s.Position_Read = 0;
                    s.Position = 0;
                    s.SetLength(0);
                    log.TraceEvent(TraceEventType.Verbose, clientPort, " Stream: {0} loop Read : count {3}  Position_Read {1}, Position_Write {2}", cors, s.Position_Read, s.Position_Write, count);
                    Monitor.Wait(l);
                    log.TraceEvent(TraceEventType.Verbose, clientPort, " Stream: {0} loop after sleep: Read : count {3} Position_Read {1}, Position_Write {2}", cors, s.Position_Read, s.Position_Write, count);
                }
                s.Position = s.Position_Read;
                chunksize = s.Read(buffer, offset, count);
                s.Position_Read += chunksize;
                if (s.Position_Read == s.Position_Write)
                {
                    s.Position_Write = s.Position_Read = 0;
                    s.Position = 0;
                    s.SetLength(0);
                }
                log.TraceEvent(TraceEventType.Verbose, clientPort, "Capacity of the memory Stream after Read {0} : {1}", cors, s.Capacity);
            }
            return chunksize;
        }
        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }
        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }
        //public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        //{
        //    //return write.BeginInvoke(buffer, offset, count, callback, state);
        //    return new FullDuplexAsyncResult<int>(
        //        delegate { Write(buffer, offset, count); return -1; },
        //        callback,
        //        state);
        //}
        //public override void EndWrite(IAsyncResult asyncResult)
        //{
        //    //write.EndInvoke(asyncResult);
        //    FullDuplexAsyncResult<int> fdasyncresult = asyncResult as FullDuplexAsyncResult<int>;
        //    if (fdasyncresult == null) throw new ArgumentException("Bad Async result");
        //    fdasyncresult.End();
        //    return;
        //}
        public override void Write(byte[] buffer, int offset, int count)
        {
            MemoryStreamWithPosition s;
            if (cors == ClientOrServer.Server) s = b.server2Client;
            else s = b.client2Server;

            object l = s.synch;
            lock (l)
            {
                log.TraceEvent(TraceEventType.Verbose, clientPort, "Capacity of the memory Stream before Write {0} : {1}", cors, s.Capacity);
                log.TraceEvent(TraceEventType.Verbose, clientPort, " mswp: {0} Write : count {3} Position_Read {1}, Position_Write {2}", cors, s.Position_Read, s.Position_Write, count);
                log.TraceEvent(TraceEventType.Verbose, clientPort, "Capacity of the buffer used by NegotiateStream :{0}", buffer.Length);
                s.Position = s.Position_Write;
                s.Write(buffer, offset, count);
                s.Position_Write += count;
                log.TraceEvent(TraceEventType.Verbose, clientPort, "Capacity of the memory Stream after Write {0} : {1}", cors, s.Capacity);
                Monitor.Pulse(l);
            }


        }
    }
    //public delegate T Function<T>();
    //public class FullDuplexAsyncResult<T> : IAsyncResult
    //{
    //    private bool m_isCompleted;
    //    private volatile ManualResetEvent m_asyncWaitHandle;
    //    private object m_sync = new object();
    //    // public volatile bool m_initialized;

    //    private readonly AsyncCallback m_callback;
    //    private readonly object m_asyncState;
    //    private Exception m_exception;
    //    private T m_result;

    //    public FullDuplexAsyncResult(Function<T> work, AsyncCallback callback, object state)
    //    {
    //        m_callback = callback;
    //        m_asyncState = state;
    //        RunWorkAsynchronously(work);
    //    }
    //    public bool IsCompleted
    //    {
    //        get { return (m_isCompleted); }
    //    }
    //    public bool CompletedSynchronously
    //    {
    //        get { return false; }
    //    }
    //    public WaitHandle AsyncWaitHandle
    //    {
    //        get
    //        {
    //            if (m_asyncWaitHandle == null)
    //            {
    //                lock (m_sync)
    //                {
    //                    if (m_asyncWaitHandle == null)
    //                    {
    //                        //if another thread is in the delegate launched by RunWorkAsynchronously
    //                        // after the test if (m_asyncWaitHandle != null), then the waitHandle could not be signaled.
    //                        // So m_isCompleted is set to true with the test with a lock. 
    //                        if (m_isCompleted) m_asyncWaitHandle = new ManualResetEvent(true);
    //                        else m_asyncWaitHandle = new ManualResetEvent(false);
    //                    }
    //                }
    //            }
    //            return m_asyncWaitHandle;
    //        }
    //    }
    //    public object AsyncState
    //    {
    //        get { return m_asyncState; }
    //    }
    //    private void RunWorkAsynchronously(Function<T> work)
    //    {
    //        ThreadPool.QueueUserWorkItem(delegate
    //        {
    //            try
    //            {
    //                m_result = work();
    //            }
    //            catch (Exception e)
    //            {
    //                m_exception = e;
    //            }
    //            finally
    //            {
    //                //m_isCompleted MUST be set before the test m_asyncWaitHandle != null (cf AsyncWaitHandle) => lock
    //                lock (m_sync)
    //                {
    //                    m_isCompleted = true;
    //                    if (m_asyncWaitHandle != null) m_asyncWaitHandle.Set();
    //                }
    //                if (m_callback != null) m_callback(this);
    //            }
    //        });
    //    }
    //    public T End()
    //    {
    //        if (!m_isCompleted)
    //        {
    //            AsyncWaitHandle.WaitOne();
    //            AsyncWaitHandle.Close();
    //        }
    //        if (m_exception != null) throw m_exception;
    //        return m_result;
    //    }
    //}
}